#include "webserver.h"
WebServer::WebServer(int port,int trigMode,int timeoutMS,bool OptLinger,
                int threadMax,bool Openlog,int Loglevel,int LogCapacity)
        :port_(port),timeoutMS_(timeoutMS),OptLinger_(OptLinger),isClose_(false),
        Timer_(new HeapTime),Thredpoll_(new ThreadPoll(threadMax)),epoller_(new Epoller())
{
    ///home/ligima/Gen_1/Process_poll_1/source暂时先用这个路径
    srcDir_ = getcwd(nullptr,256);
    assert(srcDir_);
    strcat(srcDir_,"/resource");
    Httpconn::srcDir_ = srcDir_;
    Httpconn::usercount_ = 0;

    //初始化模式
    InitEventMode(trigMode);
    //创建监听sock
    if(InitSock() == false) {   isClose_ = true;    }
    if(Openlog)
    {//此时日志为异步写入
        Log::getInstance()->init(Loglevel,"./Logs",".log",LogCapacity);
        if(isClose_ == false) 
        {   LOG_ERRNO("=========Server Init Errno=========");    }
        else
        {
            LOG_INFO("Port: %d , Openlinger: %s",port_,OptLinger_?"true":"false");
            LOG_INFO("Listen Model: %d , OpenCoon Model: %d",
                        (ListenFd_ & EPOLLIN?"ET":"LT"),
                        (coonfd_event & EPOLLIN?"ET":"LT"));
            LOG_INFO("LogSys level: %d",Loglevel);
            LOG_INFO("SrcDir: %s",Httpconn::srcDir_);
        }
    }
}
WebServer::~WebServer()
{
    close(ListenFd_);
    isClose_ = true;
    free(srcDir_);
}  
void WebServer::InitEventMode(int Mode)
{
    listen_event = EPOLLRDHUP;                  //检测 主进程 监听sock状态
    coonfd_event = EPOLLIN | EPOLLONESHOT;      //在Reactor模式下，一次完整的事务只由单个线程完成
    switch(Mode)
    {
        case 0:
        {
            break;
        }
        case 1:
        {
            listen_event |= EPOLLET;
            break;
        }
        case 2:
        {
            coonfd_event |= EPOLLET;
            break;
        }
        case 3:
        {
            listen_event |=EPOLLET;
            coonfd_event |=EPOLLET;
            break;
        }
        default:
            break;
    } 
    Httpconn::isET_ = (coonfd_event && EPOLLET);
}
void WebServer::Start()
{
    int epoll_time = -1;
    if(isClose_ == false)   
    {   LOG_INFO("=======================Server Start=======================");}
    while(isClose_ == false)
    {
        if(timeoutMS_ > 0 )
        {//获取下一次的超时检测的时间
            epoll_time = Timer_->GetNetTick();
        }
        int EventNum = epoller_->Wait_(epoll_time);

        for(int i = 0;i<EventNum;i++)
        {
            //先获取 描述符 和 事件属性
            int Fd = epoller_->GetEventFd_(i);
            int Event = epoller_->GetEvent_(i);

            if(Fd == ListenFd_)
            {
                DealListen();
            }
            else if(Event &(EPOLLRDHUP | EPOLLHUP| EPOLLERR))
            {//TPC被关闭/写操作被关闭、挂起、其他错误
                assert(user_.count(Fd) > 0);
                CloseCoon(&user_[Fd]);
            }
            else if(Event & EPOLLIN)
            {//读事件
                assert(user_.count(Fd) > 0);
                DealRead(&user_[Fd]);
            }
            else if(Event & EPOLLOUT)
            {//写事件
                assert(user_.count(Fd) > 0);
                DealWrite(&user_[Fd]);
            }
            else
            {
                LOG_ERRNO("Unexpected Events");
            }
        }
    }
}
bool WebServer::InitSock()
{
    int status;
    struct sockaddr_in Client_sockaddr;
    if(port_ >= 65535 || port_ < 1024)
    {
        LOG_ERRNO("Create sock Failed[port: %d]",port_);
        return false;
    }

    //create sock
    status = socket(AF_INET,SOCK_STREAM,0);
    if(status < 0)
    {
        LOG_ERRNO("sock(AF_INET,SOCK_STREAM,0) Failed");
        return false;
    }
    ListenFd_ = status;

    //set socke option
    //优雅退出
    struct linger optlinger;
    bzero(&optlinger,sizeof(optlinger));
    if(OptLinger_)
    {
        optlinger.l_onoff = 1;
        //延遲1s彻底关闭sock,如果数据还未发送完,则直接丢弃数据
        optlinger.l_linger = 1; 
    }
    status = setsockopt(ListenFd_,SOL_SOCKET,SO_LINGER,&optlinger,sizeof(optlinger));
    //端口复用 干掉TIME_WAIT状态
    int optval = 1;
    status = setsockopt(ListenFd_,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));
    if(status <0 )
    {
        LOG_ERRNO("Setsockopt Failed");
        close(ListenFd_);
        return false;
    }


    //bind
    bzero(&Client_sockaddr,sizeof(Client_sockaddr));
    Client_sockaddr.sin_family = AF_INET;
    Client_sockaddr.sin_port = htons(port_);
    Client_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    status = bind(ListenFd_,(struct sockaddr*)&Client_sockaddr,sizeof(Client_sockaddr));
    if(status <0 )
    {
        LOG_ERRNO("Sock Bind Failed");
        close(ListenFd_);
        return false;
    }

    //listen
    status = listen(ListenFd_,1024);
    if(status <0 )
    {
        LOG_ERRNO("sock listen Failed");
        close(ListenFd_);
        return false;
    }
    //设置 监听符的属性,只监听读事件
    status = epoller_->AddFd_(ListenFd_,listen_event|EPOLLIN);
    if(status == 0)
    {
        LOG_ERRNO("epoller AddFd_ Failed");
        close(ListenFd_);
        return false;
    }

    //在边沿触发模式下,对监听sock的属性必须为非阻塞模式,
    //否则主进程会一直阻塞在accept函数上 
    status = SetFdNonBlock(ListenFd_);
    LOG_INFO("sock port: %d",port_);
    return true;
}
void WebServer::AddClient(int fd,sockaddr_in addr)
{
    assert(fd > 0);
    user_[fd].init(fd,addr);
    if(timeoutMS_ > 0)
    {
        Timer_->add(fd,timeoutMS_,std::bind(&WebServer::CloseCoon,this,&user_[fd]));
    }
    epoller_->AddFd_(fd,coonfd_event|EPOLLIN);
    SetFdNonBlock(fd);
    LOG_INFO("Client[%d] linked in!",user_[fd].getFd());
}
void WebServer::SendError(int fd,const char * info)
{
    assert(fd > 0);
    int status = send(fd,info,strlen(info),0);
    if(status < 0)
    {
        LOG_WARN("Send Error To Client[%d] Error",fd);
    }
    close(fd);
}
void WebServer::ExtenTime(Httpconn* Client)
{//針對某个客户端的超时时间进行更新/延迟
    assert(Client != nullptr);
    if(timeoutMS_ >0 )
    {
        Timer_->adjust(Client->getFd(),timeoutMS_);
    }
}
void WebServer::CloseCoon(Httpconn* Client)
{//关闭与客户端的一个连接
//关闭sock + 关闭客户在指定文件上的内存映射
    assert(Client != nullptr);
    LOG_INFO("Client[%d] Quit",Client->getFd());
    epoller_->DelFd_(Client->getFd());
    Client->Close();    
}
void WebServer::DealListen()
{
    struct sockaddr_in Client_sockaddr;
    socklen_t addr_len = sizeof(Client_sockaddr);
    do
    {
        int status = accept(ListenFd_,(struct sockaddr*)&Client_sockaddr,&addr_len);
        if(status <=0)  //EWOULDBLOCK 、EAGIN
        {   return; }
        //else if(Httpconn::usercount_>= MAX_FD)
        //{
        //    SendError(status,"Server is full");
        //    LOG_ERRNO("Client Is Full");
        //    return;
        //}
        AddClient(status,Client_sockaddr);
        
    } while (listen_event & EPOLLET);
}
void WebServer::DealWrite(Httpconn* Client)
{
    assert(Client != nullptr);
    ExtenTime(Client);
    //这里的结构化绑定相当于 this.OnWrite(Client) 语句 执行函数调用
    //因为是类函数 所以要加上 this 指针指明类对象调用类函数这一行为
    Thredpoll_->addTask(std::bind(&WebServer::OnWrite,this,Client));
}
void WebServer::DealRead(Httpconn* Client)
{
    assert(Client != nullptr);
    ExtenTime(Client);
    Thredpoll_->addTask(std::bind(&WebServer::OnRead,this,Client));
}
void WebServer::OnRead(Httpconn* Client)
{
    assert(Client != nullptr);
    int status = -1;
    int readError = 0;
    status = Client->read(&readError);
    if(status <=0 && readError!= EAGAIN)
    {
        CloseCoon(Client);
        return;
    }
    OnProcess(Client);
}
void WebServer::OnWrite(Httpconn* Client)
{
    assert(Client != nullptr);
    int status = -1;
    int writeError = 0;
    status = Client->write(&writeError);
    if(Client->TotalWBytes() == 0)
    {//缓冲区的数据被全部聚集写入完毕
        if(Client->IsKeepAlive() == true)
        {
            epoller_->ModFd_(Client->getFd(),coonfd_event|EPOLLIN);
            return ;
        }
    }
    else if(status <=0 )
    {
        if(writeError == EAGAIN)    //缓冲区满,再次触发epoll_wait 写事件 + fd写事件
        {
            epoller_->ModFd_(Client->getFd(),coonfd_event|EPOLLOUT);
            return ;
        }
    }
    //至此,一次完整的事务/http请求 才完成
    //客户端的页面在一次请求中已经加载出了一次,无需再保持连接
    //关闭与客户端的连接
    CloseCoon(Client);
}
void WebServer::OnProcess(Httpconn* Client)
{
    assert(Client != nullptr);
    if(Client->process()==true)
    {//解析成功完成
    //将sock改为写预备 发送http回应报文
        epoller_->ModFd_(Client->getFd(),coonfd_event|EPOLLOUT);
    }
    else
    {//解析未完成,可能的原因是缓存区不够,再进行一轮读事件
        epoller_->ModFd_(Client->getFd(),coonfd_event|EPOLLIN);
    }
}
int WebServer::SetFdNonBlock(int fd)
{
    assert(fd > 0 );
    return fcntl(fd,F_SETFL,fcntl(fd,F_GETFD,0) | O_NONBLOCK);
}